package com.yanghui.dtracing.core.plugin;

import com.yanghui.dtracing.core.EnhancedInstance;
import com.yanghui.dtracing.core.WitnessClassFinder;
import com.yanghui.dtracing.core.log.ILog;
import com.yanghui.dtracing.core.log.LogManager;
import com.yanghui.dtracing.core.plugin.interceptor.*;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers;

public abstract class AbstractClassEnhancePluginDefine implements IClassEnhancePluginDefine{

    private static final ILog log = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);

    @Override
    public IConstructorMethodInterceptPoint[] buildConstructorMethodInterceptPoints() {
        return new IConstructorMethodInterceptPoint[0];
    }

    @Override
    public String[] witnessClasses() {
        return new String[]{};
    }

    @Override
    public IStaticMethodInterceptPoint[] buildStaticMethodInterceptPoints() {
        return new IStaticMethodInterceptPoint[0];
    }

    @Override
    public DynamicType.Builder<?> define(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
        return this.defineClass(builder,typeDescription,classLoader);
    }

    /**
     * 增强静态方法
     * @param builder
     * @param typeDescription
     * @param classLoader
     * @return
     */
    private DynamicType.Builder<?> enhanceClass(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader){
        IStaticMethodInterceptPoint[] staticMethodInterceptPoints = this.buildStaticMethodInterceptPoints();
        if(null != staticMethodInterceptPoints){
            for(IStaticMethodInterceptPoint point : staticMethodInterceptPoints){
                if(point.isOverrideArgs()){
                    builder = builder
                            .method(ElementMatchers.isStatic().and(point.buildMethodsMatcher()))
                            .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                            .withBinders(Morph.Binder.install(OverrideArgsCallable.class))
                                            .to(new StaticMethodInterceptWithOverrideArgs(point.methodInterceptor(),classLoader))
                                );
                }else {
                    builder = builder
                            .method(ElementMatchers.isStatic().and(point.buildMethodsMatcher()))
                            .intercept(MethodDelegation.to(new StaticMethodIntercept(point.methodInterceptor(),classLoader)));
                }
            }
        }
        return builder;
    }

    /**
     * 增强实例方法
     * @param builder
     * @param typeDescription
     * @param classLoader
     * @return
     */
    private DynamicType.Builder<?> enhanceInstance(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader){
        /**
         * 实例方法监控点
         */
        IInstanceMethodInterceptPoint[] instanceMethodInterceptPoints = this.buildInstanceMethodInterceptPoints();
        /**
         * 构造方法监控端点
         */
        IConstructorMethodInterceptPoint[] constructorInterceptPoints = this.buildConstructorMethodInterceptPoints();

        if(instanceMethodInterceptPoints != null){
            for(IInstanceMethodInterceptPoint point : instanceMethodInterceptPoints){
                if(point.isOverrideArgs()){
                    builder = builder
                            .method(ElementMatchers.not(ElementMatchers.isStatic()).and(point.buildMethodsMatcher()))
                            .intercept(
                                    MethodDelegation.withDefaultConfiguration()
                                            .withBinders(Morph.Binder.install(OverrideArgsCallable.class))
                                            .to(new InstanceMethodInterceptWithOverrideArgs(point.methodInterceptor(),classLoader))
                            );
                }else {
                    builder = builder
                            .method(ElementMatchers.not(ElementMatchers.isStatic()).and(point.buildMethodsMatcher()))
                            .intercept(MethodDelegation.to(new InstanceMethodIntercept(point.methodInterceptor(),classLoader)));
                }
            }
        }

        if(constructorInterceptPoints != null){
            for(IConstructorMethodInterceptPoint point : constructorInterceptPoints){
                builder = builder
                        .defineField(FIELD_ENHANCED,Object.class, Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE)
                        .implement(EnhancedInstance.class)
                        .intercept(FieldAccessor.ofField(FIELD_ENHANCED))
                        .constructor(point.buildConstructorMatcher())
                        .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(
                                new ConstructIntercept(point.constructorInterceptor(),classLoader)
                        )));
            }
        }
        return builder;
    }

    private DynamicType.Builder<?> defineClass(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader){
        String[] witnessClasses = this.witnessClasses();
        if(null != witnessClasses && witnessClasses.length > 0){
            for(String className : witnessClasses){
                if(!WitnessClassFinder.INSTANCE.exist(className,classLoader)){
                    log.info("不拦截：{}",typeDescription.getName());
                    return builder;
                }
            }
        }
        builder = this.enhanceClass(builder,typeDescription,classLoader);
        builder = this.enhanceInstance(builder,typeDescription,classLoader);
        return builder;
    }
}
